<template>
  <div class="dialog">
    <transition name="fade" appear>
      <div class="mask" v-if="showDialog" @click="onMaskClose" :style="maskStyle"></div>
    </transition>

    <transition :name="props.animation" appear>
      <div class="content" :style="contentStyle" v-if="showDialog" @click.self="onMaskClose">
        <component :is="componentName" @close="closeDialog"></component>
      </div>
    </transition>
  </div>
</template>

<script setup lang="ts">
import { onBeforeMount, ref, getCurrentInstance, markRaw, shallowRef } from 'vue'
import { state } from './store'

const alignMap = {
  left: 'flex-start',
  top: 'flex-start',
  right: 'flex-end',
  bottom: 'flex-end',
  center: 'center',
}

const props = defineProps({
  show: {
    type: Boolean,
    default: false,
    required: true,
  },
  animation: {
    type: String,
    default: 'fade',
    validator: (val: string) => {
      return ['fade', 'bounce', 'fade-down'].includes(val)
    },
  },
  alignX: {
    type: String,
    default: 'center',
    validator: (val: string) => {
      return ['left', 'center', 'right'].includes(val)
    },
  },
  alignY: {
    type: String,
    default: 'center',
    validator: (val: string) => {
      return ['top', 'center', 'bottom'].includes(val)
    },
  },
  maskClose: {
    type: Boolean,
    default: false,
  },
  maskColor: {
    type: String,
  },
  opacity: {
    type: Number,
    validator: (val: number) => {
      return val <= 1 && val >= 0
    },
  },
  name: {
    type: String,
    required: true,
  },
  component: {
    type: Object,
  },
})

const instance: any = getCurrentInstance()
let componentName: any

const showDialog = ref(props.show)
const maskStyle = ref({
  backgroundColor: '',
})
const contentStyle = ref({
  justifyContent: '',
  alignItems: '',
})

function onMaskClose() {
  if (props.maskClose) {
    showDialog.value = false
    for (let dia of state.dialogList) {
      if (dia.name == props.name) {
        dia.close()
        break
      }
    }
  }
}

function registerComponents() {
  if (!instance.components) {
    instance.components = {}
  }
  instance.components[props.name] = props.component
  componentName = shallowRef(props.component)
}

function closeDialog() {
  showDialog.value = false
  for (let dia of state.dialogList) {
    if (dia.name == props.name) {
      dia.close()
      break
    }
  }
}

onBeforeMount(() => {
  if (props.maskColor) {
    let maskColor = props.maskColor
    if (props.opacity && props.opacity != -1) {
      let index = maskColor.indexOf(')')
      if (index != -1) {
        maskColor = maskColor.slice(0, index) + ',' + props.opacity + ')'
      }
    }
    maskStyle.value.backgroundColor = maskColor
  }
  contentStyle.value.justifyContent = alignMap[props.alignX] || 'center'
  contentStyle.value.alignItems = alignMap[props.alignY] || 'center'
})

registerComponents()
</script>

<style scoped>
.dialog {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 13;
}

.mask {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 13;
  background-color: rgb(67, 67, 67, 0.7);
}

.content {
  display: flex;
  width: 100vw;
  height: 100vh;
  position: relative;
  z-index: 18;
}

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.8s ease;
}

.fade-enter-from,
.fade-leave-to,
.bounce-enter-from,
.bounce-leave-to {
  opacity: 0;
}

.bounce-enter-active {
  animation: bounceIn 0.5s;
}

.bounce-leave-active {
  animation: bounceOut 0.5s;
}

.fade-down-enter-active {
  animation: fadeInUp 0.5s;
}

.fade-down-leave-active {
  animation: fadeOutDown 0.5s;
}

@keyframes bounceIn {
  from,
  60%,
  75%,
  90%,
  to {
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  0% {
    opacity: 0;
    transform: scale(0.5, 0.5);
  }

  60% {
    opacity: 1;
    transform: scale(0.9, 0.9);
  }

  75% {
    transform: scale(1.05, 1.05);
  }

  90% {
    transform: scale(0.985, 0.985);
  }

  to {
    transform: scaleX(1);
  }
}

@keyframes bounceOut {
  from,
  30%,
  45%,
  90%,
  to {
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  0% {
    opacity: 0;
    transform: scale(0.98, 0.98);
  }

  30% {
    opacity: 1;
    transform: scale(0.9, 0.9);
  }

  45% {
    transform: scale(1.05, 1.05);
  }

  90% {
    transform: scale(0.7, 0.7);
  }

  to {
    transform: scale(0.58, 0.58);
  }
}

@keyframes fadeOutDown {
  0% {
    opacity: 1;
  }

  to {
    opacity: 0;
    transform: translate3d(0, 100%, 0);
  }
}

@keyframes fadeInUp {
  0% {
    opacity: 0;
    transform: translate3d(0, 100%, 0);
  }

  to {
    opacity: 1;
    transform: translateZ(0);
  }
}
</style>
